`include "vsrc/ysyx_20220337_define.v"

module ysyx_20220337_IDU(
    input             clk,
    input             rst,
    input    [31:0]   instr,
    input    [63:0]   pc_addr,
    input    [63:0]   wb_data,
    output            jump,
    output   [2:0]    RW_type,
    output            Mem_read,
    output            Mem_write,
    output   [4:0]    ALU_control,
    output            op_dw,//rv64
    output            Mem2Reg,
    output   [63:0]   data1,
    output   [63:0]   data2,
    output   [63:0]   rs2_data,
    output   [63:0]   Imm,
    output   [63:0]   dnpc
);
    wire     [4:0]    Rs1;
    wire     [4:0]    Rs2;
    wire     [4:0]    Rd;
    wire              RegWrite;
    wire     [63:0]   rs1_data;
    wire              ALU_Src1;
    wire              ALU_Src2;

    //assign branch = jump & NOT_ZERO;
    /************************************/
    /*************decode*****************/
    /************************************/
    ysyx_20220337_Decode decode(
        .instr(instr),
        .rs1_data(rs1_data),
        .rs2_data(rs2_data),
        .pc_addr(pc_addr),
        .jump(jump),
        .RW_type(RW_type),
        .Mem_Read(Mem_read),
        .Mem_Write(Mem_write),
        .Mem2Reg(Mem2Reg), 
	    .ALU_control(ALU_control),
        .op_dw(op_dw),
        .data1(data1),
        .data2(data2),
        .imme(Imm),
        .Rs1(Rs1),
        .Rs2(Rs2),
        .Rd(Rd),
        .RegWrite(RegWrite),
        .dnpc(dnpc)
    );

    /************************************/
    /*************regfile****************/
    /************************************/
    ysyx_20220337_RegisterFile regfile(
        .clk(clk),
        .rst(rst),
        .wen(RegWrite),
        .ren(1'b1),
        .waddr(Rd),
        .wdata(wb_data),
        .raddr1(Rs1),
        .rdata1(rs1_data),
        .raddr2(Rs2),
        .rdata2(rs2_data)
    );
  
endmodule

module ysyx_20220337_Decode(
    input    [31:0]   instr,
    input    [63:0]   rs1_data,
    input    [63:0]   rs2_data,
    input    [63:0]   pc_addr,
    output            jump,
    output   [2:0]    RW_type,
    output            Mem_Read,
    output            Mem_Write,
    output            Mem2Reg, 
	output   [4:0]    ALU_control,
    output            op_dw,//rv64
    output   [63:0]   data1,
    output   [63:0]   data2,
    output   [63:0]   imme,
    output   [4:0]    Rs1,
    output   [4:0]    Rs2,
    output   [4:0]    Rd,
    output            RegWrite,
    output   [63:0]   dnpc
);
	//指令拆分
    wire    [6:0]   opcode;
    wire    [2:0]   func3;
    wire    [6:0]   func7;

	assign opcode=instr[6:0];
	assign func3=instr[14:12];
	assign func7=instr[31:25];
	assign Rs1=instr[19:15];
	assign Rs2=instr[24:20];
	assign Rd =instr[11:7];

    //指令类型
    wire R_type;
	wire I_type;
	wire U_type;
	wire J_type;
	wire B_type;
	wire S_type;

    assign R_type=(instr[6:0]==`ysyx_20220337_R_type) | (instr[6:0]== 7'b0111011);
	assign I_type=(instr[6:0]==`ysyx_20220337_jalr) | (instr[6:0]==`ysyx_20220337_load) | (instr[6:0]==`ysyx_20220337_I_type) | (instr[6:0]==7'b0000011) | (instr[6:0]==7'b0011011);
	assign U_type=(instr[6:0]==`ysyx_20220337_lui) | (instr[6:0]==`ysyx_20220337_auipc);
	assign J_type=(instr[6:0]==`ysyx_20220337_jal);
	assign B_type=(instr[6:0]==`ysyx_20220337_B_type);
	assign S_type=(instr[6:0]==`ysyx_20220337_store);


	//立即数
	wire [63:0]I_imme;
	wire [63:0]U_imme;
	wire [63:0]J_imme;
	wire [63:0]B_imme;
	wire [63:0]S_imme;
	assign I_imme={{32{instr[31]}},{20{instr[31]}},instr[31:20]}; 
	assign U_imme={{32{instr[31]}},instr[31:12],{12{1'b0}}};
	assign J_imme={{32{instr[31]}},{12{instr[31]}},instr[19:12],instr[20],instr[30:21],1'b0};   
	assign B_imme={{32{instr[31]}},{20{instr[31]}},instr[7],instr[30:25],instr[11:8],1'b0};
	assign S_imme={{32{instr[31]}},{20{instr[31]}},instr[31:25],instr[11:7]}; 
    
    //操作类型
    wire load;
	wire store;
    wire instr_lui;
    wire instr_auipc;
    wire instr_jalr;
    wire instr_jal;
    wire instr_addi;
    wire instr_add;
    wire instr_addw;//rv64
    wire instr_addiw;//rv64
    wire instr_sub;
    wire instr_subw;//rv64
    wire instr_andi;
    wire instr_and;
    wire instr_ori;
    wire instr_or;
    wire instr_xori;
    wire instr_xor;
    wire instr_slli;
    wire instr_sll;
    wire instr_sllw;//rv64
    wire instr_slliw;//rv64
    wire instr_srli;
    wire instr_srl;
    wire instr_srlw;//rv64
    wire instr_srliw;//rv64
    wire instr_srai;
    wire instr_sra;
    wire instr_sraw;//rv64
    wire instr_sraiw;//rv64
    wire instr_slti;
    wire instr_slt;
    wire instr_blt;
    wire instr_sltiu;
    wire instr_sltu;
    wire instr_bltu;
    wire instr_bge;
    wire instr_bgeu;
    wire instr_beq;
    wire instr_bne;
    wire instr_mul;
    wire instr_mulh;
    wire instr_mulhsu;
    wire instr_mulhu;
    wire instr_div;
    wire instr_divu;
    wire instr_rem;
    wire instr_remu;




    assign load         =(opcode==`ysyx_20220337_load);
	assign store        =(opcode==`ysyx_20220337_store);
    assign instr_lui    =(opcode==`ysyx_20220337_lui);
    assign instr_auipc  =(opcode==`ysyx_20220337_auipc);
    assign instr_jalr   =(opcode==`ysyx_20220337_jalr);
    assign instr_jal    = J_type;
    assign instr_addi   = I_type & (func3 == 3'b0);
    assign instr_add    = R_type & (func3 == 3'b0) & (instr[31:25] == 7'b0000000);
    assign instr_addw   = R_type & (func3 == 3'b0) & (instr[31:25] == 7'b0000000) & (instr[6:0]== 7'b0011011);
    assign instr_addiw  = I_type & (func3 == 3'b0) & (instr[6:0]== 7'b0011011);
    assign instr_sub    = R_type & (func3 == 3'b0) & (instr[31:25] == 7'b0100000);
    assign instr_subw   = R_type & (func3 == 3'b0) & (instr[31:25] == 7'b0100000) & (instr[6:0]== 7'b0111011);
    assign instr_andi   = I_type & (func3 == 3'b111);
    assign instr_and    = R_type & (func3 == 3'b111) & ~instr[25];
    assign instr_ori    = I_type & (func3 == 3'b110) & instr[4];
    assign instr_or     = R_type & (func3 == 3'b110) & (instr[31:25] == 7'b0000000);
    assign instr_xori   = I_type & (func3 == 3'b100) & instr[4];
    assign instr_xor    = R_type & (func3 == 3'b100) & (instr[31:25] == 7'b0000000);
    assign instr_slli   = I_type & (func3 == 3'b001) & instr[4] & (instr[31:26] == 6'b000000);
    assign instr_sll    = R_type & (func3 == 3'b001) & (instr[31:25] == 7'b0000000);
    assign instr_sllw   = R_type & (func3 == 3'b001) & (instr[31:25] == 7'b0000000) & (instr[6:0]== 7'b0111011);
    assign instr_slliw  = I_type & (func3 == 3'b001) & (instr[31:26] == 6'b000000) & (instr[6:0]== 7'b0011011);
    assign instr_srli   = I_type & (func3 == 3'b101) & instr[4] & (instr[31:26] == 6'b000000);
    assign instr_srl    = R_type & (func3 == 3'b101) & (instr[31:25] == 7'b0000000);
    assign instr_srlw   = R_type & (func3 == 3'b101) & (instr[31:26] == 6'b000000) & (instr[6:0]== 7'b0111011);
    assign instr_srliw  = I_type & (func3 == 3'b101) & (instr[31:26] == 6'b000000) & (instr[6:0]== 7'b0011011);
    assign instr_srai   = I_type & (func3 == 3'b101) & instr[4] & (instr[31:26] == 7'b010000);
    assign instr_sra    = R_type & (func3 == 3'b101) & (instr[31:25] == 7'b0100000);
    assign instr_sraw   = R_type & (func3 == 3'b101) & (instr[31:26] == 6'b010000) & (instr[6:0]== 7'b0111011);
    assign instr_sraiw  = I_type & (func3 == 3'b101) & (instr[31:26] == 6'b010000) & (instr[6:0]== 7'b0011011);
    assign instr_slti   = I_type & (func3 == 3'b010) & instr[4];
    assign instr_slt    = R_type & (func3 == 3'b010) & (instr[31:25] == 7'b0000000);
    assign instr_blt    = B_type & (func3 == 3'b100);
    assign instr_sltiu  = I_type & (func3 == 3'b011) & instr[4];
    assign instr_sltu   = R_type & (func3 == 3'b011) & (instr[31:25] == 7'b0000000);
    assign instr_bltu   = B_type & (func3 == 3'b110);
    assign instr_bge    = B_type & (func3 == 3'b101);
    assign instr_bgeu   = B_type & (func3 == 3'b111);
    assign instr_beq    = B_type & (func3 == 3'b000);
    assign instr_bne    = B_type & (func3 == 3'b001);
    assign instr_mul    = R_type & (func3 == 3'b000) & (instr[31:25] == 7'b0000001);
    assign instr_mulh   = R_type & (func3 == 3'b001) & (instr[31:25] == 7'b0000001);
    assign instr_mulhsu = R_type & (func3 == 3'b010) & (instr[31:25] == 7'b0000001);
    assign instr_mulhu  = R_type & (func3 == 3'b011) & (instr[31:25] == 7'b0000001);
    assign instr_div    = R_type & (func3 == 3'b100) & (instr[31:25] == 7'b0000001);
    assign instr_divu   = R_type & (func3 == 3'b101) & (instr[31:25] == 7'b0000001);
    assign instr_rem    = R_type & (func3 == 3'b110) & (instr[31:25] == 7'b0000001);
    assign instr_remu   = R_type & (func3 == 3'b111) & (instr[31:25] == 7'b0000001);

    //运算类型
    wire fn_add;    //ALU +
    wire fn_sub;    //ALU -
    wire fn_and;    //ALU &&
    wire fn_or;     //ALU ||
    wire fn_xor;    //ALU ^
    wire fn_sl;     //ALU <<
    wire fn_sr;     //ALU >> 
    wire fn_sla;    //ALU <<< //没用到
    wire fn_sra;    //ALU >>>
    wire fn_slt;    //ALU < 则置位 有符号
    wire fn_sltu;   //ALU < 则置位 无符号 
    wire fn_sge;    //ALU >= 有符号
    wire fn_sgeu;   //ALU >= 无符号
    wire fn_seq;    //ALU == 有符号
    wire fn_sne;    //ALU ！= 有符号
    wire fn_mul;    //ALU *
    wire fn_mulh;   //ALU * 高位乘法
    wire fn_mulhu;  //ALU * 无符号乘法
    wire fn_mulhsu; //ALU * 有符号*无符号
    wire fn_div;    //ALU /
    wire fn_divu;   //ALU / 无符号
    wire fn_rem;    //ALU 求余数
    wire fn_remu;   //ALU 求余数 无符号

    assign fn_add   = Mem_Read | Mem_Write | instr_auipc | instr_jal | instr_jalr | U_type | instr_addi | instr_add | instr_addw | instr_addiw;
    assign fn_sub   = instr_sub | instr_subw;
    assign fn_and   = instr_andi | instr_and;
    assign fn_or    = instr_ori | instr_or;
    assign fn_xor   = instr_xori | instr_xor;
    assign fn_sl    = instr_slli | instr_sll | instr_sllw | instr_slliw;
    assign fn_sr    = instr_srli | instr_srl | instr_srlw | instr_srliw;
    assign fn_sla   = 1'b0;
    assign fn_sra   = instr_srai | instr_sra | instr_sraw | instr_sraiw;
    assign fn_slt   = instr_slti | instr_slt | instr_blt;
    assign fn_sltu  = instr_sltiu | instr_sltu | instr_bltu;
    assign fn_sge   = instr_bge;
    assign fn_sgeu  = instr_bgeu;
    assign fn_seq   = instr_beq;
    assign fn_sne   = instr_bne;
    assign fn_mul   = instr_mul;
    assign fn_mulh  = instr_mulh;
    assign fn_mulhu = instr_mulhu;
    assign fn_mulhsu= instr_mulhsu;
    assign fn_div   = instr_div;
    assign fn_divu  = instr_divu;
    assign fn_rem   = instr_rem;
    assign fn_remu  = instr_remu;

    assign ALU_control[4] = fn_mul | fn_mulh | fn_mulhu | fn_mulhsu | fn_div | fn_divu | fn_rem | fn_remu;
    assign ALU_control[3] = fn_sla | fn_sra | fn_slt | fn_sltu | fn_sge | fn_sgeu | fn_seq | fn_sne;
    assign ALU_control[2] = fn_or | fn_xor | fn_sl | fn_sr | fn_sge | fn_sgeu | fn_seq | fn_sne | fn_div | fn_divu | fn_rem | fn_remu;
    assign ALU_control[1] = fn_sub | fn_and | fn_sl | fn_sr | fn_slt | fn_sltu | fn_seq | fn_sne | fn_mulhu | fn_mulhsu | fn_rem | fn_remu;
    assign ALU_control[0] = fn_add | fn_and | fn_xor | fn_sr | fn_sra | fn_sltu | fn_sgeu | fn_sne | fn_mulh | fn_mulhsu | fn_divu | fn_remu;

    //output
    assign jump         = B_type || instr_jal || instr_jalr;
    assign RW_type      = func3;
    assign Mem_Read     = load;
    assign Mem_Write    = store;
    assign Mem2Reg      = load;
    
    assign data1        = (instr_jal | instr_jalr)? 64'h4 : (I_type | S_type | B_type | R_type)? rs1_data : imme;
    assign data2        = (instr_jal | instr_jalr | instr_auipc)? pc_addr : (B_type | R_type)? rs2_data : instr_lui? 64'h0 : imme;
    assign imme         = I_type?I_imme :
                          U_type?U_imme :
                          J_type?J_imme :
                          B_type?B_imme :
                          S_type?S_imme : 64'd0;
    assign RegWrite     = instr_jal| instr_jalr | load | I_type |R_type | U_type;
    assign dnpc         = instr_jalr ? (rs1_data + imme) : instr_auipc? imme : pc_addr + imme;
    assign op_dw        = (I_type | R_type) & instr[3];

 
endmodule

module ysyx_20220337_RegisterFile #(ADDR_WIDTH = 5, DATA_WIDTH = 64) (
  input        clk,
  input        rst,
  input        wen,
  input        ren,
  input        [ADDR_WIDTH-1:0] waddr,
  input        [DATA_WIDTH-1:0] wdata,
  input        [ADDR_WIDTH-1:0] raddr1,
  output       [DATA_WIDTH-1:0] rdata1,
  input        [ADDR_WIDTH-1:0] raddr2,
  output       [DATA_WIDTH-1:0] rdata2
);
  reg [DATA_WIDTH-1:0] rf [2**ADDR_WIDTH-1:0] ;
  integer  i ;
  always @(posedge clk) begin
    if (rst)begin
        for(i=0; i<32; i=i+1 )begin
            rf[i] <= 0; 
        end
    end
    else if (wen) rf[waddr] <= (waddr == 5'b00000) ? 64'd0  : wdata;
  end

  //assign rdata = ren? (raddr>5'b00000)? rf[raddr] : ysyx_20220337_zero_word : ysyx_20220337_zero_word;
  assign rdata1 = ren? rf[raddr1] : 64'd0;
  assign rdata2 = ren? rf[raddr2] : 64'd0;

endmodule
